import {CreateElement,PropOptions,PropType,VNode,VNodeChildren} from 'vue';
import mixins from 'vue-typed-mixins';
import {FieldGetter,Formatter,PlainObject,SizeObj} from '../../../../types';
import {QSpinner,QVirtualScroll} from 'quasar';
import Icon from '../icon';
import Checkbox from '../checkbox';
import BothScrollArea from '../both-scroll-area';
import {NO_DATA,NO_FILTER_DATA} from '../../js/pc/message';
import TreeMixin,{ComputedObj,FilterMethod,FilterType,RawObj,BeforeProcessor,NodeScope} from '../../js/pc/mixin/treeMixin';
//region 定义接口
/**
 * 获取节点显示值的方法
 * @param {NodeScope} scope	节点信息
 * @return {string}			节点显示值
 */
export interface GetCellValue{
	(scope:NodeScope):string
}
/**
 * 过滤方法的参数
 * @property {ComputedObj} meta
 * @property {FilterType} filter				当前过滤值
 * @property {GetCellValue} getCellValue	获取节点显示值的方法
 */
export interface FilterMethodParam{
	meta:ComputedObj,
	filter:FilterType,
	getCellValue:GetCellValue,
}
//endregion
/**
 * 树
 * 十万级数据, 丝滑体验
 * 百万级数据, 比较流畅
 * 配合懒加载和无末级勾选策略, 轻松加载千万级数据
 * @author 唐国雄
 * @date 2021/7/15
 * 属性----------------------
 * :inner-style			内部样式, PlainObject
 * :virtual-scroll		虚拟滚动, boolean
 * :virtual-scroll-slice-size	最小渲染项(virtual-scroll必须为true), number | string
 * :virtual-scroll-item-size	每项默认高度(virtual-scroll必须为true), number | string
 * :select-toggle		重复点击选择的表示取消, boolean
 * :loading				加载状态, boolean
 * :connectors			显示连线, boolean
 * :auto-height         自动高度
 * :data				数据列表, Array<RawObj>
 * :format				格式化, Formatter<any>
 * :obj-key				主键, string | FieldGetter<RawObj>
 * :label-key			显示字段, string | FieldGetter<NodeScope>
 * :children-key		子节点属性, string
 * :no-data-label		无数据提示, string
 * :no-results-label	无过滤结果提示, string
 * :filter				过滤值, string | FilterObj<void>
 * :filter-method		过滤方法, FilterMethod<void>
 * :tick-strategy		勾选策略, TickStrategy
 * :accordion			同一级只展开一个, boolean
 * :selectable			是否能选中, boolean
 * :selected			选中的行(支持sync), RawObj
 * :before-selected		选中前置处理器, BeforeProcessor
 * :tree-ticked			勾选的行(支持sync), MySet<RawObj>
 * :tree-expanded		展开的行(支持sync), MySet<RawObj>
 * :emit-all-ticked		发射所有勾选或者半勾选的节点, boolean
 * :no-leaf-ticking		没有末级勾选策略(用于优化大数据), boolean
 * :ticked-on-click		点击时勾选, boolean
 * :min-width			最小宽度, number
 * 事件----------------------
 * @update:selected			选中的行, RawObj
 * @update:tree-ticked		勾选的行, MySet<RawObj>
 * @update:tree-expanded	展开的行, MySet<RawObj>
 * @all-ticked				所有勾选或者半勾选的节点, Array<RawObj>
 * @tree-lazy-load			开始懒加载, {meta:ComputedObj, done:(Array<RawObj>) => void, fail:() => void}
 * @click					点击节点, {meta:ComputedObj, e:Event}
 * @tree-ticked				点击可勾选的勾选框, {meta:ComputedObj, state:boolean}
 * 方法----------------------
 * setExpandedUnlimited(opts:Array<Operation>)		设置行的展开状态(无视限制)
 * setTickedUnlimited(opts:Array<Operation>)		设置行的勾选状态(无视限制)
 * setExpanded(key:string,state:boolean)			设置行的展开状态(带校验), Promise<void>
 * setExpandeds(keys:Array<string>,state:boolean)	批量设置行的展开状态(带校验), Promise<void>
 * setMatchesExpanded(state:boolean)				设置所有匹配节点的展开状态(带校验), Promise<void>
 * expandedToNode(key:string)						展开到当前节点(带校验), Promise<void>
 * setTicked(key:string,state:boolean)				设置行的勾选状态(带校验)
 * setTickeds(keys:Array<string>,state:boolean)		批量设置行的勾选状态(带校验)
 * getMeta(key:string):ComputedObj|undefined		获取行的所有状态信息
 * getMetas():Array<ComputedObj>|undefined			获取所有行的所有状态信息
 * scrollTo(key:string)								滚动到节点(会自动展开节点), Promise<void>
 * 插槽----------------------
 * #loading					加载中, {height:number}
 * #no-data					无数据提示, {height:number}
 * #no-results				无过滤结果提示, {height:number}
 * #content					节点内容, {value:string} & NodeScope
 */
export default mixins(TreeMixin).extend({
	name:'Tree',
	props:{
		//内部样式
		innerStyle:Object as PropOptions<PlainObject>,
		//虚拟滚动
		virtualScroll:Boolean,
		//最小渲染项(virtual-scroll必须为true)
		virtualScrollSliceSize:[Number,String],
		//每项默认高度(virtual-scroll必须为true)
		virtualScrollItemSize:[Number,String],
		//重复点击选择的表示取消
		selectToggle:Boolean,
		//加载状态
		loading:Boolean,
		//显示连线
		connectors:Boolean,
		//自动高度
		autoHeight:Boolean,
		//格式化
		format:Function as PropType<Formatter<any>>,
		//显示字段
		labelKey:{
			type:[String,Function],
			default:'label',
		} as PropOptions<string|FieldGetter<NodeScope>>,
		//子节点字段
		childrenKey:{
			type:String,
			default:'children',
		},
		//没有数据时提示
		noDataLabel:{
			type:String,
			default:NO_DATA,
		},
		//没有过滤结果时提示
		noResultsLabel:{
			type:String,
			default:NO_FILTER_DATA,
		},
		//过滤方法
		filterMethod:{
			type:Function,
			default({meta,filter,getCellValue}:FilterMethodParam){
				const value=getCellValue({meta});
				return typeof filter==='string'?value.indexOf(filter)>-1:filter.test(value);
			}
		} as PropOptions<FilterMethod<FilterMethodParam>>,
		//选中前置处理器
		beforeSelected:Function as PropType<BeforeProcessor>,
		//点击时勾选
		tickedOnClick:Boolean,
		//最小宽度
		minWidth:Number,
	},
	data(){
		return {
			//组件宽度
			width:0,
			//组件高度
			height:0,
			//内容高度
			innerHeight:0,
			scrollTarget:null,
		};
	},
	computed:{
		classes():string{
			return 'lexmis-Tree'+
				(this.connectors?' lexmis--connectors':'');
		},
		realWidth():number{
			if(this.minWidth&&this.minWidth>this.width){
				return this.minWidth;
			}
			return this.width;
		},
	},
	mounted(){
		// @ts-ignore
		this.scrollTarget=this.$refs.scroll.getVScrollTarget();
	},
	methods:{
		//region 获取过滤方法的参数
		$_lexmis_getFilterMethodParam(meta:ComputedObj):FilterMethodParam{
			return {
				meta,
				filter:this.curFilter,
				getCellValue:this.getCellValue,
			};
		},
		//endregion
		//region 单个节点渲染
		$_lexmis_renderNode(h:CreateElement,computedObj:ComputedObj){
			return h('div',{
				staticClass:'lexmis_node'+
					(computedObj.selected?' lexmis--selected':''),
				key:computedObj.key,
				on:{
					click:(e:Event) => {
						this.$_lexmis_onClick(computedObj,e);
					}
				}
			},this.$_lexmis_renderNodeContent(
				h,
				computedObj,
				this.$_lexmis_getContent({
					meta:computedObj
				}),
				QSpinner,
				Icon,
				Checkbox,
			));
		},
		//endregion
		//region 节点树渲染
		$_lexmis_renderNodes(h:CreateElement,computedObjs:Array<ComputedObj>){
			if(this.virtualScroll){
				return h(QVirtualScroll,{
					staticClass:'lexmis_tree_content',
					staticStyle:{
						minWidth:this.realWidth+'px',
					},
					ref:'virtualScroll',
					props:{
						scrollTarget:this.scrollTarget,
						virtualScrollSliceSize:this.virtualScrollSliceSize||Math.ceil(this.height/29*2),
						virtualScrollItemSize:this.virtualScrollItemSize||29,
						items:computedObjs,
					},
					scopedSlots:{
						default:({item}) => this.$_lexmis_renderNode(h,item)
					}
				});
			}else{
				return h('div',{
					staticClass:'lexmis_tree_content',
					staticStyle:Object.assign({
						minWidth:this.realWidth+'px',
					},this.innerStyle),
				},computedObjs.map((item) => this.$_lexmis_renderNode(h,item)));
			}
		},
		//endregion
		//region 空数据渲染
		$_lexmis_renderEmptyContent(h:CreateElement,slot:string,content:VNodeChildren){
			const scopedSlot=this.$scopedSlots[slot];
			return h('div',{
				staticClass:'lexmis_full_content',
				staticStyle:{
					width:this.realWidth+'px',
					height:this.height+'px',
				}
			},
			scopedSlot
				?scopedSlot({height:this.height})
				:content
			);
		},
		//endregion
		//region 获取单元格实际内容(考虑插槽)
		$_lexmis_getContent(scope:NodeScope):VNodeChildren{
			const cell=this.$scopedSlots.content;
			const value=this.getCellValue(scope);
			if(cell){
				return cell({
					value,
					...scope,
				});
			}else{
				return value;
			}
		},
		//endregion
		//region 获取单元格显示值
		getCellValue(scope:NodeScope):string{
			const {meta:{rawObj}}=scope;
			let value:string;
			if(typeof this.labelKey==='string'){
				value=rawObj[this.labelKey];
			}else{
				value=this.labelKey(scope);
			}
			if(this.format){
				value=this.format(value);
			}
			return value+'';
		},
		//endregion
		//region 点击选择
		async $_lexmis_onClick(meta:ComputedObj,e:Event){
			this.$emit('click',meta,e);
			if(meta.selectable){
				let target:RawObj|null=meta.rawObj;
				if(meta.selected){
					if(this.selectToggle){
						target=null;
					}
				}
				if(this.innerSelected!==target){
					const beforeSelected=this.beforeSelected;
					if(beforeSelected&&await beforeSelected({meta})){
						return;
					}
					if(this.selected!==undefined){
						this.$emit('update:selected',target);
					}else{
						this.innerSelected=target;
					}
				}
			}
			if(this.tickedOnClick){
				this.$_lexmis_onTickedClick(meta,!meta.ticked);
			}
		},
		//endregion
		//region 滚动到节点
		scrollTo(key:string):Promise<void>{
			return new Promise((resolve) => {
				this.expandedToNode(key).then(() => {
					this.$nextTick(function(){
						const meta=this.getMeta(key);
						if(meta&&meta.visible&&meta.matchesFilter){
							// @ts-ignore
							const index=this.treeInfo.computedObjs.indexOf(meta);
							if(this.virtualScroll){
								// @ts-ignore
								this.$refs.virtualScroll.scrollTo(index,'center-force');
							}else{
								const ele=(this.$el.querySelector(`.lexmis_node:nth-child(${index+1})`) as HTMLElement);
								// @ts-ignore
								resolve(this.$refs.scroll.setVScrollPosition(ele.offsetTop-Math.ceil(this.height/2),0.5));
							}
						}
						resolve();
					});
				});
			});
		},
		//endregion
	},
	//region 渲染函数
	render(h):VNode{
		return h(BothScrollArea,{
			staticClass:this.classes,
			staticStyle:this.autoHeight
				?{
					height:this.innerHeight+'px',
				}
				:undefined,
			ref:'scroll',
			on:{
				resize:(size:SizeObj) => {
					this.width=size.width;
					this.height=size.height;
				},
				'inner-resize':(size:SizeObj) => {
					this.innerHeight=size.height;
				},
			}
		},[
			this.loading
				?this.$_lexmis_renderEmptyContent(
					h,
					'loading',
					[
						h(QSpinner,{
							staticClass:'lexmis_spinner',
						})
					]
				)
				:(
					(this.treeInfo&&this.treeInfo.computedObjs.length>0)
						?this.$_lexmis_renderNodes(h,this.treeInfo.computedObjs)
						:this.curFilter
							?this.$_lexmis_renderEmptyContent(h,'no-results',this.noResultsLabel)
							:this.$_lexmis_renderEmptyContent(h,'no-data',this.noDataLabel)
				)
		]);
	}
	//endregion
});
